Refactoring 전Refactoring 후
{code:java}
public class Movie {
public static final int CHILDREN = 2;
public static final int REGULAR = 0;
public static final int NEW_REALEASE = 1;

private String _title;
private int _priceCode;

public Movie(String title, int priceCode){
_title = title;
_priceCode = priceCode;
}

public int getPriceCode() {
return _priceCode;
}

public void setPriceCode(int arg){
_priceCode = arg;
}

public String getTitle() {
return _title;
}
}

 {code:java}
public class Rental {

   private Movie _movie;
   private int _daysRented;

   public Rental (Movie movie, int daysRented) {
      _movie = movie;
      _daysRented = daysRented;
   }

   public int getDaysRented() {
      return _daysRented;
   }

   public Movie getMovie() {
      return _movie;
   }
}


public class Customer {

   private String _name;
   private Vector _rentals = New Vector();

   public Customer (String name) {
      _name= name;
   }

   public void addRetal(Rental arg) {
      _rentals.addElement(arg);
   }

   public String getName() {
      return _name;
   }
   
   public String statement() {
      
      double totalAmount = 0;
      int frequentRenterPoints = 0;
      Enumeration rentals = _rentals.element();
      String result ="Rental Record for " + getName() + "\n";
      
      while (rentals.hasMoreElements()) {
         double thisAmount = 0;
         Rental each = (Rental)rentals.nextElement();

         //각 영화에 대한 요금 결정
         
         switch (each.getMovie().getPriceCode() {
            case Movie.REGULAR;
               thisAmount += 2;
               if (each.getDaysRented() > 2)
                  thisAmount += (each.getDaysRented() - 2) * 1.5;
               break;
            case Movie.RELEASE;
               thisAmount += each.getDaysRented() * 3;
               break;
            case Movie.CHILDREN;
               thisAmount += 1.5;
               if (each.getDaysRented() > 3)
                  thisAmount += (each.getDaysRented() - 3) * 1.5;
               break;
         }

         // 포인트(frequent renter points) 추가
         frequentRenterPoints ++;

         // 최신을 이틀이상 대여하는 경우 추가 포인트 제공
         if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) &&
             each.getDaysRented() > 1) frequentRenterPoints ++;

         // 이 대여에 대한 요금 계산 결과 표시
         result += "\t" + each.getMovie().getTitle() + "\t" +
               String.valueOf(thisAmount) + "\n";
         totalAmount += thisAmount;
      }

      //footer 추가
      result += "Amount owed is " + String.valueOf(totalAmount) + "\n";
      result += "You earned " + String.valueOf(frequentRenterPoints) +
                "frequent renter points";

      return result;
   }
                      
}

|


abstract class Price {
   abstract int getPriceCode();
   abstract double getCharge(int daysRented);
   public int getFrequentRenterPoints(int daysRented) {
      return 1;
   }
}


public class ChildrenPrice extends Price {

   public int getPriceCode() {
      return Movie.CHILDREN;
   }

   public double getCharge(int daysRented) {
      double result = 1.5;
      if (daysRented > 3)
         result += (daysRented - 3) * 1.5;
      return result;
   }

}


public class NewReleasePrice extends Price {

   public int getPriceCode() {
      return Movie.NEW_RELEASE;
   }

   public double getCharge(int daysRented) {
      double result = 2;
      if (daysRented > 2)
         result += (daysRented - 2) * 1.5;
      return result;
   }

   public int getFrequentRenterPoints(int daysRented) {
      return (daysRented > 1) ? 2 : 1;
   }
  
}


public class RegularPrice extends Price {

   public int getPriceCode() {
      return Movie.REGULAR;
   }

   public double getCharge(int daysRented) {
      return daysRented * 3;
   }

}


public class Movie {
   public static final int CHILDREN = 2;
   public static final int REGULAR = 0;
   public static final int NEW_REALEASE = 1;

   private String _title;
   private Price _price;

   public Movie(String title, int priceCode){
      _title = title;
      setPriceCode(priceCode);
   }

   public int getPriceCode() {
      return _price.getPriceCode();
   }

   public void setPriceCode(int arg){
      swith(arg) {
         case REGULAR;
            _price = new RegularPrice();
            break;
         case CHILDREN;
            _price = new ChildrenPrice();
            break;
         case NEW_RELEASE;
            _price = new NewReleasePrice();
            break;
         default:
            throw new ILLealArgumentException("Incorrect Price Code");
      }
   }

   public String getTitle() {
      return _title;
   }


   public double getCharge(int daysRented) {
      return _price.getCharge(daysRented);
   }

   public int getFrequentRenterPoints(int daysRented) {
      return _price.getFrequentRenterPoints(daysRented);
   }
 
}


public class Rental {

   private Movie _movie;
   private int _daysRented;

   public Rental (Movie movie, int daysRented) {
      _movie = movie;
      _daysRented = daysRented;
   }

   public int getDaysRented() {
      return _daysRented;
   }

   public Movie getMovie() {
      return _movie;
   }

   public double getCharge() { 
      return _movie.getCharge(_daysRented);
   }

   public int getFrequentRenterPoints() { 
      return _movie.getFrequentRenterPoints(_daysRented);
   }
}


public class Customer {

   private String _name;
   private Vector _rentals = New Vector();

   public Customer (String name) {
      _name= name;
   }

   public void addRetal(Rental arg) {
      _rentals.addElement(arg);
   }

   public String getName() {
      return _name;
   }
   
   public String statement() {
      
      Enumeration rentals = _rentals.element();
      String result ="Rental Record for " + getName() + "\n";
      
      while (rentals.hasMoreElements()) {
          Rental each = (Rental)rentals.nextElement();

         // 이 대여에 대한 요금 계산 결과 표시
         result += "\t" + each.getMovie().getTitle() + "\t" +
               String.valueOf(each.getCharge()) + "\n";
      }

      //footer 추가
      result += "Amount owed is " + String.valueOf(getTotalCharge()) + "\n";
      result += "You earned " + String.valueOf(getTotalFrequentRenterPoints()) +
                "frequent renter points";

      return result;
   }

   private double getTotalCharge() {

      double result = 0;
      Enumeration rentals = _rentals.element();
     
      while (rentals.hasMoreElements()) {
          Rental each = (Rental)rentals.nextElement();
         result += each.getCharge();
      }
      
      return result;
   }

   private int getTotalFrequentRenterPoints() {

      int result = 0;
      Enumeration rentals = _rentals.element();
     
      while (rentals.hasMoreElements()) {
          Rental each = (Rental)rentals.nextElement();
         result += each.getFrequentRenterPoints();
      }
      
      return result;
   }
                      
}

|

1. statement 메소드의 분해 및 재분배

지나치게 긴 statement 메소드의 분해
Extract Method 이용

1.1 switch문의 분리(amountFor())

1.2 amountFor() 재분배

Customer 클래스에 존재하나, Rental 클래스의 정보를 사용
대부분의 경우 메소드는 그것이 사용하는 데이터가 있는 객체에 있어야 함.
Rental 클래스로 이동(getCharge()메소드 명 변경)

1.3 포인트 계산 부분 추출

포인트 계산 부분도 분리 하고, Rental 클래스로 이동(getFrequentRenterPoints()).

1.4 임시변수 제거하기

Replace Temp with Query를 사용하여, totalAmount와 frequentRenterPoints를 질의 메소드로 변경
totalAmount => getTotalCharge()
frequentRenterPoints => getTotalFrequentRenterPoints()

2. 조건문을 다형성(polymorphism)으로 바꾸기

다른 객체의 속성에 기반한 switch문을 사용하는 것은 좋지 않음.
Rental 클래스의 getCharge() 메소드를 Movie 클래스로 이동
getFrequentRenterPoints() 메소드도 동일한 작업 진행

2.1 상속

Movie 클래스에 State Pattern을 이용
요금코드 객체를 생성하고 이를 서브클래싱(Regular, Children, NewRelease).

2.1.1 Replace Type Code with State/Strategy 이용

switch문에 사용된 타입별 코드를 스테이트 패턴으로 바꿈.
새로운 Price 클래스 추가, getPriceCode() 추상 메소드 만들고 서브클래스에서 구현

2.1.2 Move Method 이용

getCharge() 메소드에 적용
Price 클래스로 이동

2.1.3 Replace Conditional with Polymorphism 이용

case 문을 하나씩 취해 getCharge() 메소드를 오버라이드.
Price 클래스의 getCharge() 메소드를 추상메소드 선언.